iT邦幫忙

2021 iThome 鐵人賽

DAY 10
1

除了一對多的關聯方式以外,資料和資料間也有可能是多對多的關聯方式。

比方說常見的用戶標籤(tag) 系統,就是一種多對多的關聯:每個標籤可能對應多個用戶,而每個用戶也可能被標記上多個不同標籤。

要怎麼設計這樣的關聯呢?我們一起來看看

中間表

多對多關係的處理方式,比較建議的做法是設計一個中間表,每筆資料紀錄

比方說,如果我們的資料是

user tag
Alice admin, registered, author
Bob customer, unregistered
Carol customer, registered, reader

那我們的資料表內容可能會是

|user|
|---|---|
|id|name|
|1|Alice|
|2|Bob|
|3|Carol|

|tag|
|---|---|
|id|name|
|1|admin|
|2|registered|
|3|author|
|4|customer|
|5|unregistered|
|6|reader|

|user_tag|
|---|---|
|user_id|tag_id|
|1|1|
|1|2|
|1|3|
|2|4|
|2|5|
|3|4|
|3|2|
|3|6|

假設我們需要的用戶標籤系統,tag 資料表和 DAO 物件如下

object Tags : IntIdTable() {  
    val name = varchar("name", 50)  
}  
  
class Tag(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<Tag>(Tags)  
    var name by Tags.name  
}

我們定義中間表 user_tag 如下

object UserTag : IntIdTable() {  
    val user = reference("user", Users)  
    val tag = reference("tag", Tags)  
}

利用 via 這個內綴,我們讓 usertag 物件與 UserTag 資料表進行關聯

class Tag(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<Tag>(Tags)  
    var name by Tags.name  
 	var users by User via UsersTags  
}

class User(id: EntityID<Int>) : IntEntity(id) {  
    companion object : IntEntityClass<User>(Users)  
    var name by Users.name  
 	var tags by Tag via UsersTags  
}

並且定義 usertag 內容

SchemaUtils.create(Users)  
SchemaUtils.create(Tags)  
val alice = User.new {  
 	name = "Alice"  
}  
val bob = User.new {  
 	name = "Bob"  
}  
val carol = User.new {  
 	name = "Carol"  
}  
val admin = Tag.new {  
 	name = "admin"  
}  
val registered = Tag.new {  
 	name = "registered"  
}  
val author = Tag.new {  
 	name = "author"  
}  
val customer = Tag.new {  
 	name = "customer"  
}  
val unregistered = Tag.new {  
 	name = "unregistered"  
}  
val reader = Tag.new {  
 	name = "reader"  
}

定義完成之後,我們就可以以 DAO 的方式,進行 usertag 的關聯了

alice.tags = SizedCollection(listOf(admin, registered, author))  
bob.tags = SizedCollection(listOf(customer, unregistered))  
carol.tags = SizedCollection(listOf(customer, registered, reader))

定義完成之後,我們可以透過 user 來找到所有對應的 tag

carol.tags.forEach { println("tag: ${it.name}") }

上面這段程式碼會印出

tag: customer
tag: registered
tag: reader

我們也可以透過 tag 來找到對應的 user

registered.users.forEach { println("user: ${it.name}") }

上面這段程式碼會印出

user: Alice
user: Carol

透過 DAO 的方式,以物件的思維,來實作多對多的資料關聯語法,個人認為比起透過 query 關聯的方式來說,更加直觀並且好操作許多。


上一篇
[Day 09] 資料和資料之間的一對多關聯
下一篇
[Day 11] 多對多關聯的變形:Parent-Child reference
系列文
Kotlin 怎麼操作資料庫?談談 Kotlin Exposed 框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言